#include "part_group.h"
#include "part_die.h"
#include "part2d.h"

// forces
#include "p2d_force_linear.h"
#include "p2d_force_path.h"
#include "p2d_force_plane.h"
#include "p2d_force_sphereex.h"
//#include "p2d_force_sphereinc.h"
#include "p2d_force_steer.h"
#include "p2d_force_attractor.h"


#include "script/scripthelper.h"
#include "util/log.h"		    // log
#include "util/common_macros.h"		// macros
#include "util/mmgr.h"		    // Needs to come in early

#include "num/rand.h"

#include "graphics/graphics.h"
#include "graphics/graphics_util.h"
#include "script/write_lua.h"
#ifdef _ENABLE_LUABIND
#include <luabind/luabind.hpp>
#include <luabind/copy_policy.hpp>
#endif //_ENABLE_LUABIND

#include <algorithm>

#include <cassert>
using namespace part2d;
using namespace script;
using namespace LuaPlus;

PartGroup::PartGroup(Part2D*					pSystem) : 
	m_pSystem(pSystem),
    m_Particles(0),
	m_MaxC(0), 
	m_PartC(0),
    m_LastActive(0),
    m_Additive(false),
	m_LockRect(true),
	m_LockSize(false),
	m_StretchSpeed(0),
	m_IsActive(true),
	m_SizeToMin(0.1f,0.01f),
	m_SizeToMax(0.1f,0.01f),
	m_Lifespan(0.4f,1.0f),
	m_GenRate(1/60.0f),
	m_GenTime(0),
	m_pOnDie(NULL),
	m_RenderDebug(false),
	m_IsSecondary(false),
	m_TriggerIndex(-1),
	m_SizeScale(1.0f)
{

	for (int i=0; i<COLOR_TABLE; i++)
	{
		m_ColorA[i] = 0xFFFFFFFF;
	}

	char	Buffer[256];
	sprintf(Buffer, "GROUP_%i", m_pSystem->GroupC()+1);
	m_Name = Buffer;
}

PartGroup::~PartGroup()
{
	UTIL_SAFE_DELETEA(m_Particles);
	UTIL_SAFE_DELETE(m_pOnDie);
    for (unsigned int i=0; i<m_Forces.size(); i++)
    {
        UTIL_SAFE_DELETE(m_Forces[i]);
    }
}

bool PartGroup::Save(script::WriteLua& WL)
{
	WL.StartTable();
		WL.WritePair("Image",			m_Image);
		WL.WritePair("MaxC", m_MaxC);
		WL.WritePair("Additive",		m_Additive);
		WL.WritePair("LockRect",		m_LockRect);
		WL.WritePair("LockSize",		m_LockSize);
		WL.WritePair("FuzzyBirth",		m_FuzzyBirth);
		WL.WritePair("SizeFromMin",		m_SizeFromMin);
		WL.WritePair("SizeFromMax",		m_SizeFromMax);
		WL.WritePair("SizeToMin",		m_SizeToMin);
		WL.WritePair("SizeToMax",		m_SizeToMax);
		WL.WritePair("Lifespan",		m_Lifespan);
		WL.WritePair("MinSpeed",		m_MinSpeed);
		WL.WritePair("MaxSpeed",		m_MaxSpeed);
		WL.WritePair("StretchSpeed",	m_StretchSpeed);
		WL.WritePair("MinSpeedMag",		m_MinSpeedMag);
		WL.WritePair("GenRate",			m_GenRate);
		WL.WritePair("IsSecondary",		m_IsSecondary);
		WL.WritePair("TriggerIndex",	m_TriggerIndex);

		if (!m_BaseColors.empty())
		{
			WL.StartTable("Color");
				for (unsigned int i=0; i<m_BaseColors.size(); i++)
				{
					WL.WriteNL("{ %f, %f, %f, %f }, ", m_BaseColors[i].x, m_BaseColors[i].y, m_BaseColors[i].z, m_BaseColors[i].w);
				}
			WL.CloseTable();
		}
		if (m_pOnDie)
		{
			WL.StartTable("PartDie");
				WL.WritePair("Type", m_pOnDie->GetName());
				WL.WritePair("Group", m_pOnDie->GetGroup());
				WL.WritePair("Probability", (float)(m_pOnDie->GetProbability() / 100.0f));
			WL.CloseTable(true);
		}

	WL.CloseTable(true);
	return true;
}

bool PartGroup::Load(LuaPlus::LuaObject& Data)
{
    if (Data.IsTable())
    {
		m_MaxC				= ScriptHelper::GetTableInt(Data, "MaxC", 100);
		m_Additive			= ScriptHelper::GetTableBool(Data, "Additive", false);
		m_LockRect			= ScriptHelper::GetTableBool(Data, "LockRect", false);
		m_LockSize			= ScriptHelper::GetTableBool(Data, "LockSize", false);
		m_RenderDebug		= ScriptHelper::GetTableBool(Data, "RenderDebug", false);
		m_FuzzyBirth		= ScriptHelper::GetTableVec2(Data, "FuzzyBirth", math::Vec2(0,0));
		m_SizeFromMin		= ScriptHelper::GetTableVec2(Data, "SizeFromMin", math::Vec2(0,0));
		m_SizeFromMax		= ScriptHelper::GetTableVec2(Data, "SizeFromMax", math::Vec2(0,0));
		m_SizeToMin			= ScriptHelper::GetTableVec2(Data, "SizeToMin", math::Vec2(0.1f,0.01f));
		m_SizeToMax			= ScriptHelper::GetTableVec2(Data, "SizeToMax", math::Vec2(0.1f,0.01f));
		m_Lifespan			= ScriptHelper::GetTableVec2(Data, "Lifespan", math::Vec2(0.4f,1.0f));
		m_MinSpeed			= ScriptHelper::GetTableVec2(Data, "MinSpeed", math::Vec2(0,0));
		m_MaxSpeed			= ScriptHelper::GetTableVec2(Data, "MaxSpeed", math::Vec2(0.1f,0.01f));
		m_MinSpeedMag		= ScriptHelper::GetTableFloat(Data, "MinSpeedMag", -1);
		m_StretchSpeed		= ScriptHelper::GetTableFloat(Data, "StretchSpeed", 0);
		m_GenRate			= ScriptHelper::GetTableFloat(Data, "GenRate", 1/60.0f);

		m_IsSecondary		= ScriptHelper::GetTableBool(Data, "IsSecondary", false);
		m_TriggerIndex		= ScriptHelper::GetTableInt(Data, "TriggerIndex", -1);


		m_Image	= ScriptHelper::GetTableString(Data, "Image", "");
		SetImage(m_Image);

		// forces
        LuaPlus::LuaObject Forces = ScriptHelper::GetObj(Data, "Forces");
        if (Forces.IsTable())
        {   
            unsigned int Count = Forces.GetCount();
            for (unsigned int i=1; i<=Count; i++)
            {
                P2DForce* pForce = GetForce(Forces.GetByIndex(i));
            }
        }

		// final initalization

		LuaObject       Color = Data.GetByName("Color");
		if (Color.IsTable())
		{
			m_BaseColors.reserve(Color.GetCount());
			for (unsigned int i=1; i<=(unsigned int)Color.GetCount(); i++)
			{
				LuaPlus::LuaObject TempObject = Color.GetByIndex(i);
				m_BaseColors.push_back(ScriptHelper::GetVec4(TempObject, math::Vec4(1,1,1,1)));	

			}
		}

		m_pOnDie = PartDie::GetPartDie(Data.GetByName("PartDie"), m_pSystem);
	}
	RecalcColors();
	SetMaxC(m_MaxC);

	return true;
}

void		PartGroup::SetParticleCB(PARTICLE_CALLBACKS Set, ParticleCallback pCB, void *pData)
{
	m_CallbackA[Set].pCB = pCB;
	m_CallbackA[Set].pData = pData;
}


P2DForce*	PartGroup::CreateForce(const std::string& Name)
{
    P2DForce* pForce = NULL;
	std::string	Type = Name;
    std::transform(Type.begin(), Type.end(), Type.begin(), tolower);
    if (Type == "attractor")    pForce = new P2DForceAttractor(); else
    if (Type == "linear")       pForce = new P2DForceLinear (); else
    if (Type == "killplane")    pForce = new P2DForceKillPlane(); else
    if (Type == "sphereex")		pForce = new P2DForceSphereEx(); else
	if (Type == "path")			pForce = new P2DForcePath();  else
	if (Type == "steer")		pForce = new P2DForceSteer();  

	if (pForce)
	{
		m_Forces.push_back(pForce);
	}

	return pForce;
}

void		PartGroup::DeleteForce(P2DForce*	pForce)
{
	for (unsigned int i=0; i<m_Forces.size(); i++)
	{
		if (m_Forces[i] == pForce)
		{
			delete pForce;
			m_Forces.erase(m_Forces.begin()+i);
			return;
		}
	}
}


P2DForce* PartGroup::GetForce(LuaPlus::LuaObject  _LObj)
{
    if (!_LObj.IsTable()) return NULL;
    std::string     Type = script::ScriptHelper::GetTableString(_LObj, "Type", "Linear");
    P2DForce* pForce = CreateForce(Type);

    if (pForce)
    {
        if (!pForce->Load(_LObj))
        {
            DeleteForce(pForce);
			pForce = NULL;
        }
    }


    return pForce;
}

void	PartGroup::Clone(PartGroup* pClone)
{
	pClone->m_MaxC = m_MaxC;
	pClone->m_Additive = m_Additive;
	pClone->m_LockRect = m_LockRect;
	pClone->m_LockSize = m_LockSize;
	pClone->m_FuzzyBirth = m_FuzzyBirth;
	pClone->m_SizeFromMin = m_SizeFromMin;
	pClone->m_SizeFromMax = m_SizeFromMax;
	pClone->m_SizeToMin = m_SizeToMin;
	pClone->m_SizeToMax = m_SizeToMax;
	pClone->m_Lifespan = m_Lifespan;
	pClone->m_MinSpeed = m_MinSpeed;
	pClone->m_MaxSpeed = m_MaxSpeed;
	pClone->m_MinSpeedMag = m_MinSpeedMag;
	pClone->m_StretchSpeed = m_StretchSpeed;
	pClone->m_GenRate = m_GenRate;
	pClone->m_IsSecondary = m_IsSecondary;
	pClone->m_TriggerIndex = m_TriggerIndex;

	pClone->m_Image = m_Image;
	pClone->SetImage(m_Image);

	// final initalization
	if (!m_BaseColors.empty())
	{
		pClone->m_BaseColors.reserve(m_BaseColors.size());
		for (unsigned int i = 0; i < m_BaseColors.size(); i++)
			pClone->m_BaseColors.push_back(m_BaseColors[i]);
	}

	pClone->m_pOnDie = m_pOnDie ? PartDie::GetPartDie(m_pOnDie, pClone->m_pSystem) : NULL;

	pClone->RecalcColors();
	pClone->SetMaxC(m_MaxC);

	// copy forces
	for (unsigned int i=0; i<m_Forces.size(); i++)
	{
		pClone->m_Forces.push_back(m_Forces[i]->Clone());
	}
}


void	PartGroup::SetImage(const std::string& Image)
{
	if (!Image.empty()) 
	{
		m_Image = Image;
		m_Sprite.Create(m_Image);
	}
}


void	PartGroup::SetMaxC(int	Count)
{
	m_MaxC = Count;
	UTIL_SAFE_DELETEA(m_Particles);
	if (m_MaxC>0) 
	{
		m_Particles = new Particle2D[m_MaxC];
		for ( int i=0; i<m_MaxC; i++)
		{
			m_Particles[i].Active = false;
		}
	}
	m_PartC = 0;
	m_LastActive = 0;
}

void	PartGroup::SetSize(const math::Vec2& FromMin, const math::Vec2& FromMax, const math::Vec2& ToMin, const math::Vec2& ToMax)
{
	m_SizeFromMin = FromMin;
	m_SizeFromMax = FromMax;
	m_SizeToMin = ToMin;
	m_SizeToMax = ToMax;
}

void	PartGroup::SetInitialSpeed(const math::Vec2& Min, const math::Vec2& Max, float MaxMag)
{
	m_MinSpeed = Min;
	m_MaxSpeed = Max;
	m_MinSpeedMag = MaxMag;
}

const std::string&		PartGroup::GetImage() const
{
	return m_Image;
}

void	PartGroup::RecalcColors()
{
	if (m_BaseColors.size() > 0)
	{

		int			Index = 0;
		int			Count = COLOR_TABLE/((int)m_BaseColors.size());
		for (unsigned int i=0; i<m_BaseColors.size(); i++)
		{
			math::Vec4 	P1 = m_BaseColors[i];
			math::Vec4 	P2 = P1;
			if (i+1 < m_BaseColors.size())
			{	
				P2 = m_BaseColors[i+1];
			}			
			for (int j=0; j<Count; j++)
			{
				float	Fraction = j/(float)Count;

				math::Vec4 Res = P1 + (P2-P1)*Fraction;
				m_ColorA[Index++] = ColorfRGBA(Res.x, Res.y, Res.z, Res.w);
				assert(Index <= COLOR_TABLE);
			}
		}
		math::Vec4	From = m_BaseColors[m_BaseColors.size()-1];
		for (int i=Index; i<COLOR_TABLE; i++)
		{		
			m_ColorA[i] = ColorfRGBA(From.x, From.y, From.z, From.w);
		}
	} else
	{
		// empty colors.. just set to white
		for (int i=0; i<COLOR_TABLE; i++)
		{		
			m_ColorA[i] = 0xFFFFFFFF;
		}
	}
}

bool	PartGroup::IsInside(const math::Vec2& Pos)
{
	for (int i=0; i<m_MaxC; i++)
	{
		Particle2D* pPart = &m_Particles[i];
		if (pPart->Active)
		{
			float		Fraction	= pPart->Life.GetProgress();
			math::Vec2	Size		= (pPart->FromSize + (pPart->DeltaSize*Fraction)) ;
			math::Vec2	From		= pPart->Pos - (Size * 0.5f);
			math::Vec2	To			= pPart->Pos + (Size * 0.5f);
			if (UTIL_RANGE(Pos.x, From.x, To.x) && UTIL_RANGE(Pos.y, From.y, To.y))
				return true;
		}
	}
	return false;
}

void	PartGroup::SetSizeScale(float SizeScale)
{
	m_SizeScale = SizeScale;
}

bool PartGroup::Render(const math::Vec2& Offset, float Scale, const math::Vec2& Min, const math::Vec2& Max)
{
	if (!m_IsActive) return false;
	if (m_PartC == 0) return false;
	if (!m_Sprite.IsValid()) return false;
	if (!m_Particles) return false;
	Graphics::Vertex2D*	VertA = Graphics::GetTempVert();

	if (m_RenderDebug)
	{
		// iterate through forces
		for (unsigned int i=0; i<m_Forces.size(); i++)
		{
			m_Forces[i]->RenderDebug(m_pSystem->GetPos() - Offset);
		}
	}

	ui::SpriteInstance*		pSpriteInst = m_Sprite.GetSpriteInst();
	m_Sprite.Tick(0);

	int	Index = 0;
	Graphics* pG = GetGraphics();

	if (m_Additive)
    {
		GetGraphics()->SetupAdditiveAlpha(true);
	} 

	for (int i=0; i<m_MaxC; i++)
	{
		Particle2D* pPart = &m_Particles[i];
		if (pPart->Active)
		{
			float		Fraction	= pPart->Life.GetProgress();
			math::Vec2	Size		= (pPart->FromSize + (pPart->DeltaSize*Fraction)) * Scale;
			Size *= m_SizeScale;
			Size.x *= m_SizeScale;
			Size.y *= m_SizeScale * GraphicsUtil::H2W;

			math::Vec2	From		= (pPart->Pos - Offset) * Scale;
			math::Vec2	HalfSize	= ((pPart->Dir*Size.y) + (pPart->Dir*m_StretchSpeed*pPart->Speed))*0.5f;
			HalfSize.x *= m_SizeScale;
			HalfSize.y *= m_SizeScale * GraphicsUtil::H2W;

			int			ColorIndex = (int)(COLOR_TABLE*Fraction);
			UTIL_CLAMP(ColorIndex, 0, COLOR_TABLE-1);
			COLOR		c	 = m_ColorA[ColorIndex];

			math::Vec2	UV1(0.01f,0.01f), UV2(0.99f,0.99f);
			if (pSpriteInst)
			{
				pSpriteInst->GetUV(pPart->AnimationTime, UV1, UV2);
			}

			Index+=GraphicsUtil::FillLine(From-HalfSize, From+HalfSize, Size.x * 0.5f, c, c, UV1, UV2, &VertA[Index]);

			if (Index+6 >= Graphics::MAX_TEMP_VERT) 
			{
				m_Sprite.SetTexture();
				pG->DrawVertex2D(VertA, Index);
				Index = 0;
			}
		}
	}
	if (Index > 0 ) 
	{
		m_Sprite.SetTexture();
		pG->DrawVertex2D(VertA, Index);
	}

	if (m_Additive)
    {
		GetGraphics()->SetupAdditiveAlpha(false);
    }
	return true;
}


bool PartGroup::ShouldCreateParticle(float dt)
{
	if (m_GenRate <= 0) return false;
	
	m_GenTime+=dt;
	if (m_GenTime > m_GenRate)
	{
		m_GenTime -= m_GenRate;
		return true;
	}
	return false;
}

bool	PartGroup::TickCreation(float dt, const math::Vec2& Pos, const math::Vec2& Size)
{
	if (!m_IsActive) return false;
	m_Sprite.Tick(0);
	if (!m_Sprite.IsValid()) return false;
	if (!m_Particles) return false;

	// update timer
	if (ShouldCreateParticle(dt))
	{
		TriggerParticle(num::RAND32::Instance().RandVec2(Pos, Pos+Size));
		return true;
	}
	return false;
}


bool PartGroup::Tick(float dt)
{
	if (!m_IsActive) return false;
	if (m_PartC == 0) return false;
	if (!m_Sprite.IsValid()) return false;
	if (!m_Particles) return false;



	// iterate through forces
	for (unsigned int i=0; i<m_Forces.size(); i++)
	{
		m_Forces[i]->MoveParticles(m_pSystem->GetPos(), m_Particles, m_MaxC, dt);
	}

	ui::SpriteInstance*		pSpriteInst = m_Sprite.GetSpriteInst();

	for (int i=0; i<m_MaxC; i++)
	{
		Particle2D* pPart = &m_Particles[i];
		if (pPart->Active)
		{
			pPart->Life.Tick(dt);
			if (pPart->Life.IsReady()) 
				pPart->Kill = true;
			

			// apply speeds
			pPart->Pos += pPart->Speed * dt * pPart->Dir;

			// update animation
			int	LoopC = 0;
			if (pSpriteInst)	pSpriteInst->UpdateTime(LoopC, pPart->AnimationTime, dt, true);

			if (pPart->Kill)
			{
				pPart->Active = false;
				m_PartC--;
				if (m_pOnDie)	
					m_pOnDie->ParticleDie(pPart);
				if (m_TriggerIndex >= 0 && m_TriggerIndex <m_pSystem->GroupC())
				{
					PartGroup* pGroup =	m_pSystem->GetGroup(m_TriggerIndex);
					if (pGroup && pGroup != this)		// this would cause infinite particles!
					{	
						pGroup->TriggerParticle(pPart->Pos, pPart->Dir);
					}
				}
				if (m_CallbackA[PC_DEAD].pCB)
				{
					m_CallbackA[PC_DEAD].pCB(pPart, m_CallbackA[PC_DEAD].pData);
				}
			}
		}
	}
	if (m_Collision.GetGeomC() > 0)
	{
		m_Collision.Collide(m_Particles, m_MaxC);
	}
	return true;
}


Particle2D*	  PartGroup::TriggerParticle(const math::Vec2& ScreenPos)
{
	Particle2D*	 pPart = GetFreeParticle();
	if (!pPart) return NULL;
	num::RAND32& Rnd = num::RAND32::Instance();

	pPart->Pos			= ScreenPos + num::RAND32::Instance().RandVec2(-m_FuzzyBirth, m_FuzzyBirth);
	pPart->FromSize		= Rnd.RandVec2(m_SizeFromMin, m_SizeFromMax);
	if (!m_LockSize)
		pPart->DeltaSize= Rnd.RandVec2(m_SizeToMin, m_SizeToMax) - pPart->FromSize;
	else
		pPart->DeltaSize= math::Vec2();
	pPart->Active		= true;
	pPart->Kill			= false;
	pPart->AnimationTime = 0.0f;
	pPart->Life.SetLimit(Rnd.RandFloat(m_Lifespan.x, m_Lifespan.y));
	SetSpeed(pPart, GetInitialSpeed());

	if (m_LockRect)
	{
		pPart->FromSize.y  = pPart->FromSize.x;
		pPart->DeltaSize.y  = pPart->DeltaSize.x;
	}

	m_PartC++;
	return pPart;
}

void		PartGroup::SetSpeed(Particle2D*	 pPart, const math::Vec2& Sp)
{
	pPart->Dir			= Sp;
	pPart->Speed		= pPart->Dir.Magnitude();
	if (m_MinSpeedMag > 0) 
	{
		pPart->Speed = UTIL_HIGH(pPart->Speed, m_MinSpeedMag);
	}
	if (pPart->Dir.Magnitude() <= 0.001f)
	{
		pPart->Dir = math::Vec2(1, 800.0f/600.0f);
	}

	pPart->Dir.Normalize();

}

math::Vec2	PartGroup::GetInitialSpeed() const
{
	num::RAND32& Rnd = num::RAND32::Instance();
	return Rnd.RandVec2(m_MinSpeed, m_MaxSpeed);
}

Particle2D*	 PartGroup::TriggerParticle(const math::Vec2& ScreenPos, const math::Vec2& InitialDir)
{
	Particle2D*	 pPart = TriggerParticle(ScreenPos );
	if (pPart)
	{
		pPart->Dir		= InitialDir;
		pPart->Speed	= pPart->Dir.Magnitude();
		if (pPart->Speed <= 0)
			pPart->Dir.x = pPart->Dir.y = 1.0f;
		pPart->Dir.Normalize();
	
	}
	return pPart;
}

void		PartGroup::ClearParticles()
{
	for (int i=0; i<m_MaxC; i++)
		m_Particles[i].Active = false;
	m_PartC = 0;
	m_LastActive = 0;

}

Particle2D*	 PartGroup::TriggerParticleAbs(const math::Vec2& ScreenPos, const math::Vec2& InitialDir)
{
	Particle2D*	 pPart = TriggerParticle(ScreenPos );
	if (pPart)
	{
		pPart->Pos		= ScreenPos;
		pPart->Dir		= InitialDir;
		pPart->Speed	= pPart->Dir.Magnitude();
		pPart->Dir.Normalize();
	}
	return pPart;
}


Particle2D*	PartGroup::GetFreeParticle()
{
	if (m_PartC >= m_MaxC) return NULL;	// no valid particles
	for (int i=m_LastActive; i<m_MaxC; i++)
	{
		if (!m_Particles[i].Active)
		{
			m_LastActive = i;
			return &m_Particles[i];
		}
	}
	for (unsigned int i=0; i<m_LastActive; i++)
	{
		if (!m_Particles[i].Active)
		{
			m_LastActive = i;
			return &m_Particles[i];
		}
	}
	return NULL;	// should never get this far
}


void PartGroup::Register(LuaPlus::LuaState* _pScript)
{
#ifdef _ENABLE_LUABIND
	
	using namespace luabind;	
    lua_State * L = _pScript->GetCState ( );

	module(L)
    [

    luabind::class_< PartGroup >( "PartGroup" )
		.def( "SetMaxC",			&PartGroup::SetMaxC)
		.def( "GetMaxC",			&PartGroup::GetMaxC)
		.def( "SetFuzzyBirth",		&PartGroup::SetFuzzyBirth)
		.def( "GetFuzzyBirth",		&PartGroup::GetFuzzyBirth,	copy(result))
		.def( "SetSize",			&PartGroup::SetSize)
		.def( "GetSizeFromMin",		&PartGroup::GetSizeFromMin,	copy(result))
		.def( "GetSizeFromMax",		&PartGroup::GetSizeFromMax,	copy(result))
		.def( "GetSizeToMin",		&PartGroup::GetSizeToMin,	copy(result))
		.def( "GetSizeToMax",		&PartGroup::GetSizeToMax,	copy(result))
		.def( "GetMinSpeed",		&PartGroup::GetMinSpeed,	copy(result))
		.def( "GetMaxSpeed",		&PartGroup::GetMaxSpeed,	copy(result))
		.def( "GetMinSpeedMag",		&PartGroup::GetMinSpeedMag)
		.def( "SetInitialSpeed",	&PartGroup::SetInitialSpeed)
		.def( "GetLifespan",		&PartGroup::GetLifespan,	copy(result))	// const math::Vec2&		GetLifespan() const;
		.def( "SetLifespan",		&PartGroup::SetLifespan)
		.def( "SetAdditive",		&PartGroup::SetAdditive)
		.def( "GetAdditive",		&PartGroup::GetAdditive)
		.def( "SetLockRect",		&PartGroup::SetLockRect)
		.def( "GetLockRect",		&PartGroup::GetLockRect)
		.def( "SetLockSize",		&PartGroup::SetLockSize)
		.def( "GetLockSize",		&PartGroup::GetLockSize)
	    
		.def( "GetStretchSpeed",	&PartGroup::GetStretchSpeed)
		.def( "SetStretchSpeed",	&PartGroup::SetStretchSpeed)

		.def( "AddColor",			&PartGroup::AddColor3f)
		.def( "ClearColors",		&PartGroup::ClearColors)
		.def( "ClearParticles",		&PartGroup::ClearParticles)
   
		.def( "RecalcColors",		&PartGroup::RecalcColors)
		.def( "SetImage",			&PartGroup::SetImage)
		.def( "GetImage",			&PartGroup::GetImage)

		.def( "IsActive",			&PartGroup::IsActive)
		.def( "SetActive",			&PartGroup::SetActive)

		.def( "SetGenRate",			&PartGroup::SetGenRate)
		.def( "GetGenRate",			&PartGroup::GetGenRate)
	];
#endif // _ENABLE_LUABIND
}
